<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>几个小优化方式 | TBlog</title>
    <meta name="description" content="TBlog with vitePress">
    <link rel="stylesheet" href="/interview/assets/style.9156a4ed.css">
    <link rel="modulepreload" href="/interview/assets/Home.b86443e2.js">
    <link rel="modulepreload" href="/interview/assets/app.49accf37.js">
    <link rel="modulepreload" href="/interview/assets/WebPack_optimize.md.545698f9.lean.js">
    <link rel="modulepreload" href="/interview/assets/app.49accf37.js">
    <link rel="icon" href="/favicon.ico">
    <meta name="twitter:title" content="几个小优化方式 | TBlog">
    <meta property="og:title" content="几个小优化方式 | TBlog">
  </head>
  <body>
    <div id="app"><!--[--><div class="theme"><header class="nav-bar" data-v-7161a24b><div class="sidebar-button" data-v-7161a24b><svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class></path></svg></div><a class="nav-bar-title" href="/interview/" aria-label="TBlog, back to home" data-v-7161a24b data-v-4a583abe><!----> TBlog</a><div class="flex-grow" data-v-7161a24b></div><div class="nav" data-v-7161a24b><nav class="nav-links" data-v-7161a24b data-v-15acbf05><!--[--><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:66" target="_blank" rel="noopener noreferrer" data-v-641633f9>Music <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:80" target="_blank" rel="noopener noreferrer" data-v-641633f9>CMC <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://juejin.cn/user/2163479676978734/posts" target="_blank" rel="noopener noreferrer" data-v-641633f9>掘金 <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://gitee.com/tu_zhiwei98" target="_blank" rel="noopener noreferrer" data-v-641633f9>Gitee <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><!--]--><!----><!----></nav></div><!--[--><!--]--></header><aside class="sidebar" data-v-6b49cdcd><nav class="nav-links nav" data-v-6b49cdcd data-v-15acbf05><!--[--><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:66" target="_blank" rel="noopener noreferrer" data-v-641633f9>Music <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:80" target="_blank" rel="noopener noreferrer" data-v-641633f9>CMC <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://juejin.cn/user/2163479676978734/posts" target="_blank" rel="noopener noreferrer" data-v-641633f9>掘金 <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://gitee.com/tu_zhiwei98" target="_blank" rel="noopener noreferrer" data-v-641633f9>Gitee <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><!--]--><!----><!----></nav><!--[--><!--]--><ul class="sidebar-links" data-v-6b49cdcd><!--[--><li class="sidebar-link"><p class="sidebar-link-item">Vue3.2X</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/">基本语法&amp;使用</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/VueX">VueX4.x</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/Router">Router4.x</a><!----></li><li class="sidebar-link"><p class="sidebar-link-item">部分源码解析</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/reactive">响应式系统</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/source/Watch">Watch</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/source/nextTick">nextTick</a><!----></li></ul></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">WebPack</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/">基础配置</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item active" href="/interview/WebPack/optimize">优化方案</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#使用-happypack">使用 HappyPack</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#happypack-原理">HappyPack 原理</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#使用-tree-shaking">使用 Tree Shaking</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#压缩代码">压缩代码</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#压缩-es6">压缩 ES6</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#区分环境">区分环境</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#公共代码抽离">公共代码抽离</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#输出分析">输出分析</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#官方的可视化分析工具">官方的可视化分析工具</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#webpack-bundle-analyzer">webpack-bundle-analyzer</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#使用自动刷新">使用自动刷新</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#文件监听">文件监听</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#文件监听工作原理">文件监听工作原理</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#进行分包处理-xxxx-xxx">进行分包处理 xxxx/xxx/</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/common">常见的Loader以及Plugin</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/custom">自定义Loader以及Plugin</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/advanced">手写简单的MY_Webpack</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Javascript</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Javascript/">基本语法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Javascript/advanced">高级语法</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">TypeScript</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/">基本语法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/combat">业务实战</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/advanced">高阶使用</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">浏览器相关</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/">基本原理</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/plugin">浏览器插件</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/advanced">深入原理</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">计算机网络</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Network/">计算机网络</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Network/security">前端安全</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">HTML/CSS</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/CSS/">CSS</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/HTML/">HTML</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Vite2.X</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vite/">基础配置</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vite/optimize">原理以及优化</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Node</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Node/">基本知识</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Node/pit">踩坑记录</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">其他前端工具</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Git/">Git</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Jenkins/">Jenkins</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/npm/">包管理工具</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">踩坑记录以及经验</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Pit/">踩坑</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/solution/">一些解决方案</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">面经</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Interview/">xxx面试</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Interview/two">xxX面试</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">个人学习历程</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/">个人情况说明</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/pre">入行工作前</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/one">工作第一年</a><!----></li></ul></li><!--]--></ul><!--[--><!--]--></aside><!-- TODO: make this button accessible --><div class="sidebar-mask"></div><main class="page" data-v-7eddb2c4><div class="container" data-v-7eddb2c4><!--[--><!--]--><div style="position:relative;" class="content" data-v-7eddb2c4><div><h1 id="几个小优化方式"><a class="header-anchor" href="#几个小优化方式" aria-hidden="true">#</a> 几个小优化方式</h1><p>启用缓存</p><ul><li><p>babel 缓存</p><ul><li>cacheDirectory: true (第二次构建时,会读取之前的缓存)</li></ul></li><li><p>文件资源缓存</p><ul><li>如果文件在缓存期内,更新了代码之后不能看到实时的效果</li><li>方案: 将文件名称,设置为哈希名称, 名称发生变化时, 就加载最新的内容</li></ul></li><li><p>webpack 的哈希值</p><ul><li><p>[hash] (每次 webpack 打包生成的 Hash 值)</p></li><li><p>[chunkhash] (不同 chunk 的 hash 值不同--同一次打包可能生成不同的 chunk)</p></li><li><p>[contenthash] (不同的内容的 hash 值不同- 同一个 chunk 中可能有不同的内容)</p></li></ul></li></ul><img src="/interview/assets/hash.5a75b78c.png" style="zoom:50%;"><h2 id="使用-happypack"><a class="header-anchor" href="#使用-happypack" aria-hidden="true">#</a> 使用 HappyPack</h2><p>因为 javaScript 是单线程模型,要想发挥多核 CPU 的能力，只能通过多进程去实现，而无法通过多线程实现。</p><p><a href="https://github.com/amireh/happypack" target="_blank" rel="noopener noreferrer">HappyPack</a> 就能让 Webpack 做到这点，它把任务分解给多个子进程去并发的执行，子进程处理完后再把结果发送给主进程。</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> ExtractTextPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;extract-text-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> HappyPack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;happypack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  module<span class="token operator">:</span> <span class="token punctuation">{</span>
    rules<span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span>
        test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.js$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
        <span class="token comment">// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例</span>
        use<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;happypack/loader?id=babel&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token comment">// 排除 node_modules 目录下的文件，node_modules 目录下的文件都是采用的 ES5 语法，没必要再通过 Babel 去转换</span>
        exclude<span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;node_modules&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token comment">// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例</span>
        test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.css$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
        use<span class="token operator">:</span> ExtractTextPlugin<span class="token punctuation">.</span><span class="token function">extract</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
          use<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;happypack/loader?id=css&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">HappyPack</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token comment">// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件</span>
      id<span class="token operator">:</span> <span class="token string">&quot;babel&quot;</span><span class="token punctuation">,</span>
      <span class="token comment">// 如何处理 .js 文件，用法和 Loader 配置中一样</span>
      loaders<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;babel-loader?cacheDirectory&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token comment">// ... 其它配置项</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">HappyPack</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      id<span class="token operator">:</span> <span class="token string">&quot;css&quot;</span><span class="token punctuation">,</span>
      <span class="token comment">// 如何处理 .css 文件，用法和 Loader 配置中一样</span>
      loaders<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;css-loader&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ExtractTextPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      filename<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[name].css</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>以上代码有两点重要的修改：</p><ul><li>在 Loader 配置中，所有文件的处理都交给了 <code>happypack/loader</code> 去处理，使用紧跟其后的 querystring <code>?id=babel</code> 去告诉 <code>happypack/loader</code> 去选择哪个 HappyPack 实例去处理文件。</li><li>在 Plugin 配置中，新增了两个 HappyPack 实例分别用于告诉 <code>happypack/loader</code> 去如何处理 .js 和 .css 文件。选项中的 <code>id</code> 属性的值和上面 querystring 中的 <code>?id=babel</code> 相对应，选项中的 <code>loaders</code> 属性和 Loader 配置中一样。</li></ul><p>在实例化 HappyPack 插件的时候，除了可以传入 <code>id</code> 和 <code>loaders</code> 两个参数外，HappyPack 还支持如下参数：</p><ul><li><code>threads</code> 代表开启几个子进程去处理这一类型的文件，默认是 3 个，类型必须是整数。</li><li><code>verbose</code> 是否允许 HappyPack 输出日志，默认是 <code>true</code>。</li><li><code>threadPool</code> 代表共享进程池，即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务，以防止资源占用过多，相关代码如下：</li></ul><div class="language-js"><pre><code><span class="token keyword">const</span> HappyPack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;happypack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 构造出共享进程池，进程池中包含5个子进程</span>
<span class="token keyword">const</span> happyThreadPool <span class="token operator">=</span> HappyPack<span class="token punctuation">.</span><span class="token function">ThreadPool</span><span class="token punctuation">(</span><span class="token punctuation">{</span> size<span class="token operator">:</span> <span class="token number">5</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">HappyPack</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token comment">// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件</span>
      id<span class="token operator">:</span> <span class="token string">&quot;babel&quot;</span><span class="token punctuation">,</span>
      <span class="token comment">// 如何处理 .js 文件，用法和 Loader 配置中一样</span>
      loaders<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;babel-loader?cacheDirectory&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token comment">// 使用共享进程池中的子进程去处理任务</span>
      threadPool<span class="token operator">:</span> happyThreadPool<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">HappyPack</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      id<span class="token operator">:</span> <span class="token string">&quot;css&quot;</span><span class="token punctuation">,</span>
      <span class="token comment">// 如何处理 .css 文件，用法和 Loader 配置中一样</span>
      loaders<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;css-loader&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token comment">// 使用共享进程池中的子进程去处理任务</span>
      threadPool<span class="token operator">:</span> happyThreadPool<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">ExtractTextPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      filename<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[name].css</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>接入 HappyPack 后，你需要给项目安装新的依赖：</p><div class="language-bash"><pre><code><span class="token function">npm</span> i -D happypack
</code></pre></div><p>安装成功后重新执行构建你就会看到以下由 HappyPack 输出的日志：</p><div class="language-javascript"><pre><code>Happy<span class="token punctuation">[</span>babel<span class="token punctuation">]</span><span class="token operator">:</span> Version<span class="token operator">:</span> <span class="token number">4.0</span><span class="token number">.0</span><span class="token operator">-</span>beta<span class="token punctuation">.</span><span class="token number">5.</span> Threads<span class="token operator">:</span> <span class="token number">3</span>
Happy<span class="token punctuation">[</span>babel<span class="token punctuation">]</span><span class="token operator">:</span> All set<span class="token punctuation">;</span> signaling webpack to proceed<span class="token punctuation">.</span>
Happy<span class="token punctuation">[</span>css<span class="token punctuation">]</span><span class="token operator">:</span> Version<span class="token operator">:</span> <span class="token number">4.0</span><span class="token number">.0</span><span class="token operator">-</span>beta<span class="token punctuation">.</span><span class="token number">5.</span> Threads<span class="token operator">:</span> <span class="token number">3</span>
Happy<span class="token punctuation">[</span>css<span class="token punctuation">]</span><span class="token operator">:</span> All set<span class="token punctuation">;</span> signaling webpack to proceed<span class="token punctuation">.</span>
</code></pre></div><p>说明你的 HappyPack 配置生效了，并且可以得知 HappyPack 分别启动了 3 个子进程去并行的处理任务。</p><h3 id="happypack-原理"><a class="header-anchor" href="#happypack-原理" aria-hidden="true">#</a> HappyPack 原理</h3><p>在整个 Webpack 构建流程中，最耗时的流程可能就是 Loader 对文件的转换操作了，因为要转换的文件数据巨多，而且这些转换操作都只能一个个挨着处理。 HappyPack 的核心原理就是把这部分任务分解到多个进程去并行处理，从而减少了总的构建时间。</p><p>从前面的使用中可以看出所有需要通过 Loader 处理的文件都先交给 <code>happypack/loader</code> 去处理，收集到了这些文件的处理权后 HappyPack 就好统一分配。</p><p>每通过 <code>new HappyPack()</code> 实例化一个 HappyPack 其实就是告诉 HappyPack 核心调度器如何通过一系列 Loader 去转换一类文件，并且可以指定如何给这类转换操作分配子进程。</p><p>核心调度器的逻辑代码在主进程中，也就是运行着 Webpack 的进程中，核心调度器会把一个个任务分配给当前空闲的子进程，子进程处理完毕后把结果发送给核心调度器，它们之间的数据交换是通过进程间通信 API 实现的。</p><p>核心调度器收到来自子进程处理完毕的结果后会通知 Webpack 该文件处理完毕。</p><h2 id="使用-tree-shaking"><a class="header-anchor" href="#使用-tree-shaking" aria-hidden="true">#</a> 使用 Tree Shaking</h2><p>Tree Shaking 可以用来剔除 JavaScript 中用不上的死代码.它依赖静态的 ES6 模块化语法，例如通过 <code>import</code> 和 <code>export</code> 导入导出</p><ul><li>return 之后的代码</li><li>只声明,而未使用 的函数</li><li>只引入,未使用的代码</li></ul><p>因为 ES6 模块化语法是静态的（导入导出语句中的路径必须是静态的字符串，而且不能放入其它代码块中），这让 Webpack 可以简单的分析出哪些 <code>export</code> 的被 <code>import</code> 过了。 如果你采用 ES5 中的模块化，例如 <code>module.export={...}</code>、<code>require(x+y)</code>、<code>if(x){require(&#39;./util&#39;)}</code>，Webpack 无法分析出哪些代码可以剔除。</p><p><code>til.js</code> 源码：</p><div class="language-js"><pre><code><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">funcA</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">funB</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">;</span>
</code></pre></div><p><code>main.js</code> 源码：</p><div class="language-js"><pre><code><span class="token keyword">import</span> <span class="token punctuation">{</span> funcA <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;./util.js&quot;</span><span class="token punctuation">;</span>
<span class="token function">funcA</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>Tree Shaking 后的 <code>util.js</code>：</p><div class="language-js"><pre><code><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">funcA</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre></div><p>目前的 Tree Shaking 还有些的局限性，经实验发现：</p><ol><li><p>不会对 entry 入口文件做 Tree Shaking。</p></li><li><p>不会对<a href="https://webpack.wuhaolin.cn/4%E4%BC%98%E5%8C%96/4-12%E6%8C%89%E9%9C%80%E5%8A%A0%E8%BD%BD.html" target="_blank" rel="noopener noreferrer">异步分割</a>出去的代码做 Tree Shaking。</p></li></ol><p>想使用 Tree Shaking 就需要保留 ES6 语法,所以需要配置 babel 进行配置</p><div class="language-json"><pre><code><span class="token punctuation">{</span>
  <span class="token property">&quot;presets&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">[</span>
      <span class="token string">&quot;env&quot;</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token property">&quot;modules&quot;</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token comment">// 关闭 babel的模块转换功能</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre></div><div class="language-javascript"><pre><code><span class="token comment">// 优化策略</span>
optimization<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token comment">// 标记未被使用的代码</span>
usedExports<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token comment">// 删除 usedExports  标记的未使用的代码</span>
minimize<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
minimizer<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token keyword">new</span> <span class="token class-name">TerserPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>   <span class="token comment">//  webpack 5 自带  开箱即用 不需要下载 只需要导入就可以使用</span>
<span class="token punctuation">}</span>
</code></pre></div><p>另一种实现方式 -- sideEffect</p><ul><li><p>​ 副作用</p><ul><li><p>无副作用: 如果一个模块单纯的导入导出变量,那它就是无副作用</p></li><li><p>有副作用: 如果一个模块还修改其他模块或者全局的一些东西,就有副作用</p><ul><li><p>修改全局变量</p></li><li><p>在原型上扩展方法</p></li><li><p>css 的引入</p></li></ul></li></ul></li><li><p>sideEffect 的作用: 把未使用并且无副作用的模块一并删除</p></li><li><p>开去副作用 在 webpack.config.js 中 optimization.sideEffects:true</p></li><li><p>标识代码是否有副作用 (package.json)</p><ul><li>&quot;sideEffect&quot; <ul><li>false 所有的代码都没有副作用 (告诉 webpack 可以安全的删除未用的 export)</li><li>true 所有代码都有副作用</li><li>数组 (告诉 webpack 那些模块有副作用,不删除) <ul><li>可以使用相对路径或者绝对路径</li><li>[&#39;./src/wp.js&#39;, &#39;*.css&#39;]</li></ul></li></ul></li></ul></li></ul><p>如果使用了很多的第三方库 需要选择 ES6 模块化语法的 需要进行配置</p><div class="language-js"><pre><code>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  resolve<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件</span>
    mainFields<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;jsnext:main&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;browser&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;main&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="压缩代码"><a class="header-anchor" href="#压缩代码" aria-hidden="true">#</a> 压缩代码</h2><p>目前最成熟的 JavaScript 代码压缩工具是 <a href="https://github.com/mishoo/UglifyJS2" target="_blank" rel="noopener noreferrer">UglifyJS</a> ， 它会分析 JavaScript 代码语法树，理解代码含义，从而能做到诸如去掉无效代码、去掉日志输出代码、缩短变量名等优化。</p><p>要在 Webpack 中接入 UglifyJS 需要通过插件的形式，目前有两个成熟的插件，分别是：</p><ul><li>UglifyJsPlugin：通过封装 UglifyJS 实现压缩。</li><li>ParallelUglifyPlugin：多进程并行处理压缩</li></ul><p>常用的配置</p><ul><li><code>sourceMap</code>：是否为压缩后的代码生成对应的 Source Map，默认为不生成，开启后耗时会大大增加。一般不会把压缩后的代码的 Source Map 发送给网站用户的浏览器，而是用于内部开发人员调试线上代码时使用。</li><li><code>beautify</code>： 是否输出可读性较强的代码，即会保留空格和制表符，默认为是，为了达到更好的压缩效果，可以设置为 <code>false</code>。</li><li><code>comments</code>：是否保留代码中的注释，默认为保留，为了达到更好的压缩效果，可以设置为 <code>false</code>。</li><li><code>compress.warnings</code>：是否在 UglifyJs 删除没有用到的代码时输出警告信息，默认为输出，可以设置为 <code>false</code> 以关闭这些作用不大的警告。</li><li><code>drop_console</code>：是否剔除代码中所有的 <code>console</code> 语句，默认为不剔除。开启后不仅可以提升代码压缩效果，也可以兼容不支持 <code>console</code> 语句 IE 浏览器。</li><li><code>collapse_vars</code>：是否内嵌定义了但是只用到一次的变量，例如把 <code>var x = 5; y = x</code> 转换成 <code>y = 5</code>，默认为不转换。为了达到更好的压缩效果，可以设置为 <code>true</code>。</li><li><code>reduce_vars</code>： 是否提取出出现多次但是没有定义成变量去引用的静态值，例如把 <code>x = &#39;Hello&#39;; y = &#39;Hello&#39;</code> 转换成 <code>var a = &#39;Hello&#39;; x = a; y = b</code>，默认为不转换。为了达到更好的压缩效果，可以设置为 <code>true</code>。</li></ul><p>也就是说，在不影响代码正确执行的前提下，最优化的代码压缩配置为如下：</p><div class="language-js"><pre><code><span class="token keyword">const</span> UglifyJSPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack/lib/optimize/UglifyJsPlugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token comment">// 压缩输出的 JS 代码</span>
    <span class="token keyword">new</span> <span class="token class-name">UglifyJSPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      compress<span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token comment">// 在UglifyJs删除没有用到的代码时不输出警告</span>
        warnings<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token comment">// 删除所有的 `console` 语句，可以兼容ie浏览器</span>
        drop_console<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token comment">// 内嵌定义了但是只用到一次的变量</span>
        collapse_vars<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token comment">// 提取出出现多次但是没有定义成变量去引用的静态值</span>
        reduce_vars<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      output<span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token comment">// 最紧凑的输出</span>
        beautify<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token comment">// 删除所有的注释</span>
        comments<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>从以上配置中可以看出 Webpack 内置了 UglifyJsPlugin，需要指出的是 UglifyJsPlugin 当前采用的是 <a href="https://github.com/mishoo/UglifyJS2" target="_blank" rel="noopener noreferrer">UglifyJS2</a> 而不是老的 <a href="https://github.com/mishoo/UglifyJS" target="_blank" rel="noopener noreferrer">UglifyJS1</a>， 这两个版本的 UglifyJS 在配置上有所区别，看文档时注意版本。</p><p>除此之外 Webpack 还提供了一个更简便的方法来接入 UglifyJSPlugin，直接在启动 Webpack 时带上 <code>--optimize-minimize</code> 参数，即 <code>webpack --optimize-minimize</code>， 这样 Webpack 会自动为你注入一个带有默认配置的 UglifyJSPlugin。</p><blockquote><p>本实例<a href="http://webpack.wuhaolin.cn/4-8%E5%8E%8B%E7%BC%A9%E4%BB%A3%E7%A0%81-ES5.zip" target="_blank" rel="noopener noreferrer">提供项目完整代码</a></p></blockquote><h3 id="压缩-es6"><a class="header-anchor" href="#压缩-es6" aria-hidden="true">#</a> 压缩 ES6</h3><p>虽然当前大多数 JavaScript 引擎还不完全支持 ES6 中的新特性，但在一些特定的运行环境下已经可以直接执行 ES6 代码了，例如最新版的 Chrome、ReactNative 的引擎 JavaScriptCore。</p><p>运行 ES6 的代码相比于转换后的 ES5 代码有如下优点：</p><ul><li>一样的逻辑用 ES6 实现的代码量比 ES5 更少。</li><li>JavaScript 引擎对 ES6 中的语法做了性能优化，例如针对 <code>const</code> 申明的变量有更快的读取速度。</li></ul><p>所以在运行环境允许的情况下，我们要尽可能的使用原生的 ES6 代码去运行，而不是转换后的 ES5 代码。</p><p>在你用上面所讲的压缩方法去压缩 ES6 代码时，你会发现 UglifyJS 会报错退出，原因是 UglifyJS 只认识 ES5 语法的代码。 为了压缩 ES6 代码，需要使用专门针对 ES6 代码的 <a href="https://github.com/mishoo/UglifyJS2/tree/harmony" target="_blank" rel="noopener noreferrer">UglifyES</a>。</p><p>UglifyES 和 UglifyJS 来自同一个项目的不同分支，它们的配置项基本相同，只是接入 Webpack 时有所区别。 在给 Webpack 接入 UglifyES 时，不能使用内置的 UglifyJsPlugin，而是需要单独安装和使用最新版本的 <a href="https://github.com/webpack-contrib/uglifyjs-webpack-plugin" target="_blank" rel="noopener noreferrer">uglifyjs-webpack-plugin</a>。 安装方法如下：</p><div class="language-bash"><pre><code><span class="token function">npm</span> i -D uglifyjs-webpack-plugin@beta
</code></pre></div><p>Webpack 相关配置代码如下：</p><div class="language-js"><pre><code><span class="token keyword">const</span> UglifyESPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;uglifyjs-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">UglifyESPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token comment">// 多嵌套了一层</span>
      uglifyOptions<span class="token operator">:</span> <span class="token punctuation">{</span>
        compress<span class="token operator">:</span> <span class="token punctuation">{</span>
          <span class="token comment">// 在UglifyJs删除没有用到的代码时不输出警告</span>
          warnings<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token comment">// 删除所有的 `console` 语句，可以兼容ie浏览器</span>
          drop_console<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token comment">// 内嵌定义了但是只用到一次的变量</span>
          collapse_vars<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token comment">// 提取出出现多次但是没有定义成变量去引用的静态值</span>
          reduce_vars<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        output<span class="token operator">:</span> <span class="token punctuation">{</span>
          <span class="token comment">// 最紧凑的输出</span>
          beautify<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token comment">// 删除所有的注释</span>
          comments<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>同时，为了不让 babel-loader 输出 ES5 语法的代码，需要去掉 <code>.babelrc</code> 配置文件中的 <code>babel-preset-env</code>，但是其它的 Babel 插件，比如 <code>babel-preset-react</code> 还是要保留， 因为正是 <code>babel-preset-env</code> 负责把 ES6 代码转换为 ES5 代码</p><h2 id="区分环境"><a class="header-anchor" href="#区分环境" aria-hidden="true">#</a> 区分环境</h2><ol><li><p>在开发过程中需要方便调试的环境</p></li><li><p>发布到线上给用户使用的运行环境</p><p>区分环境打包有两种方式</p><ul><li><p>通过 webpack --env.production</p></li><li><p>webpack.config.js 中判断 env</p><div class="language-javascript"><pre><code><span class="token comment">// webpack.config.js</span>
modlue<span class="token punctuation">.</span><span class="token function-variable function">export</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">env<span class="token punctuation">.</span>argv</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>env<span class="token punctuation">.</span>production<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 生产环境配置</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 开发环境配置</span>
<span class="token punctuation">}</span>
</code></pre></div></li></ul></li></ol><ul><li><p>通过配置文件区分</p></li><li><p>webpack.dev.config.js</p></li><li><p>webpack.prod.config.js</p><div class="language-javascript"><pre><code><span class="token comment">// DefinePlugin 是webpack 内置的插件 ,提供一个全局的变量  是一个代码片段</span>
<span class="token keyword">const</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webapck&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">new</span> <span class="token class-name">webpack<span class="token punctuation">.</span>DefinePlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token comment">//  开发环境下的接口地址</span>
  <span class="token constant">API_BASE_URL</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token string">&quot;http://apiprod.example.com&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div></li></ul><p>不同环境虽然都是经过同一套源码编译出来的,但是代码内容很不一样,具体差异有以下几点</p><ul><li><p>​ 线上代码被压缩过</p></li><li><p>​ 开发模式有开发日志</p></li><li><p>​ 开发用的代码所连接的后端数据接口地址也可能和线上环境不同，因为要避免开发过程中造成对线上数据的影响。</p><p>通过 merge 插件 可以合并我们的 webpack 配置对象</p></li></ul><div class="language-javascript"><pre><code><span class="token keyword">const</span> <span class="token punctuation">{</span> merge <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack-merge&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> baseConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.base.js&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token function">merge</span><span class="token punctuation">(</span>baseConfig<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  mode<span class="token operator">:</span> <span class="token string">&quot;development&quot;</span><span class="token punctuation">,</span>
  devtool<span class="token operator">:</span> <span class="token string">&quot;eval-cheap-module-source-map&quot;</span><span class="token punctuation">,</span>
  devServer<span class="token operator">:</span> <span class="token punctuation">{</span>
    port<span class="token operator">:</span> <span class="token string">&quot;4399&quot;</span><span class="token punctuation">,</span>
    hot<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    compress<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token comment">// contentBase: path.join(__dirname, &#39;/public&#39;),</span>
    <span class="token comment">// watchContentBase: true,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="公共代码抽离"><a class="header-anchor" href="#公共代码抽离" aria-hidden="true">#</a> 公共代码抽离</h2><p>提取出来公共的文件在 webpack.config.js 新增配置项</p><div class="language-javascript"><pre><code>optimization<span class="token operator">:</span> <span class="token punctuation">{</span>
  splitChunks<span class="token operator">:</span> <span class="token punctuation">{</span>
    chunks<span class="token operator">:</span> <span class="token string">&quot;all&quot;</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">//  -------&gt;&gt;   在输出目录下会生成 vendors-node_modules_xxxx</span>
</code></pre></div><h2 id="输出分析"><a class="header-anchor" href="#输出分析" aria-hidden="true">#</a> 输出分析</h2><p>由于 webpack 输入出的代码基本上没有啥可读性,并且文件很大,我们可以借助可视化的分析工具,这些可视化工具,可以用图形的方式更加直观的展示出来</p><p>webpack 在启动时 支持两个参数</p><ul><li><code>--profile</code>：记录下构建过程中的耗时信息；</li><li><code>--json</code>：以 JSON 的格式输出构建结果，最后只输出一个 <code>.json</code> 文件，这个文件中包括所有构建相关的信息。</li></ul><p>在启动 Webpack 时带上以上两个参数，启动命令如下 <code>webpack --profile --json &gt; stats.json</code>，你会发现项目中多出了一个 <code>stats.json</code> 文件。 这个 <code>stats.json</code> 文件是给后面介绍的可视化分析工具使用的。</p><blockquote><p><code>webpack --profile --json</code> 会输出字符串形式的 JSON，<code>&gt; stats.json</code> 是 UNIX/Linux 系统中的管道命令、含义是把 <code>webpack --profile --json</code> 输出的内容通过管道输出到 <code>stats.json</code> 文件中。</p></blockquote><h3 id="官方的可视化分析工具"><a class="header-anchor" href="#官方的可视化分析工具" aria-hidden="true">#</a> 官方的可视化分析工具</h3><p>Webpack 官方提供了一个可视化分析工具 <a href="http://webpack.github.io/analyse/" target="_blank" rel="noopener noreferrer">Webpack Analyse</a>，它是一个在线 Web 应用。</p><p>它分为了六大板块，分别是：</p><ul><li><strong>Modules</strong>：展示所有的模块，每个模块对应一个文件。并且还包含所有模块之间的依赖关系图、模块路径、模块 ID、模块所属 Chunk、模块大小；</li><li><strong>Chunks</strong>：展示所有的代码块，一个代码块中包含多个模块。并且还包含代码块的 ID、名称、大小、每个代码块包含的模块数量，以及代码块之间的依赖关系图；</li><li><strong>Assets</strong>：展示所有输出的文件资源，包括 <code>.js</code>、<code>.css</code>、图片等。并且还包括文件名称、大小、该文件来自哪个代码块；</li><li><strong>Warnings</strong>：展示构建过程中出现的所有警告信息；</li><li><strong>Errors</strong>：展示构建过程中出现的所有错误信息；</li><li><strong>Hints</strong>：展示处理每个模块的过程中的耗时。</li></ul><h3 id="webpack-bundle-analyzer"><a class="header-anchor" href="#webpack-bundle-analyzer" aria-hidden="true">#</a> webpack-bundle-analyzer</h3><ul><li>打包出的文件中都包含了什么；</li><li>每个文件的尺寸在总体中的占比，一眼看出哪些文件尺寸大；</li><li>模块之间的包含关系；</li><li>每个文件的 Gzip 后的大小。</li></ul><p>接入 webpack-bundle-analyzer 的方法很简单，步骤如下：</p><ol><li>安装 webpack-bundle-analyzer 到全局，执行命令 <code>npm i -g webpack-bundle-analyzer</code>；</li><li>按照上面提到的方法生成 <code>stats.json</code> 文件；</li><li>在项目根目录中执行 <code>webpack-bundle-analyzer</code> 后，浏览器会打开对应网页看到以上效果。</li></ol><h2 id="使用自动刷新"><a class="header-anchor" href="#使用自动刷新" aria-hidden="true">#</a> 使用自动刷新</h2><h2 id="文件监听"><a class="header-anchor" href="#文件监听" aria-hidden="true">#</a> 文件监听</h2><p>文件监听是在发现源码文件发生变化时，自动重新构建出新的输出文件。</p><p>Webpack 官方提供了两大模块，一个是核心的 <a href="https://www.npmjs.com/package/webpack" target="_blank" rel="noopener noreferrer">webpack</a>，一个是在使用 DevServer](<a href="https://webpack.wuhaolin.cn/1%E5%85%A5%E9%97%A8/1-6%E4%BD%BF%E7%94%A8DevServer.html" target="_blank" rel="noopener noreferrer">https://webpack.wuhaolin.cn/1入门/1-6使用DevServer.html</a>) 中提到的 <a href="https://www.npmjs.com/package/webpack-dev-server" target="_blank" rel="noopener noreferrer">webpack-dev-server</a> 扩展模块。 而文件监听功能是 webpack 模块提供的。</p><div class="language-js"><pre><code>module<span class="token punctuation">.</span>export <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token comment">// 只有在开启监听模式时，watchOptions 才有意义</span>
  <span class="token comment">// 默认为 false，也就是不开启</span>
  watch<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token comment">// 监听模式运行时的参数</span>
  <span class="token comment">// 在开启监听模式时，才有意义</span>
  watchOptions<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 不监听的文件或文件夹，支持正则匹配</span>
    <span class="token comment">// 默认为空</span>
    ignored<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
    <span class="token comment">// 监听到变化发生后会等300ms再去执行动作，防止文件更新太快导致重新编译频率太高</span>
    <span class="token comment">// 默认为 300ms</span>
    aggregateTimeout<span class="token operator">:</span> <span class="token number">300</span><span class="token punctuation">,</span>
    <span class="token comment">// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的</span>
    <span class="token comment">// 默认每隔1000毫秒询问一次</span>
    poll<span class="token operator">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>要让 Webpack 开启监听模式，有两种方式：</p><ul><li>在配置文件 <code>webpack.config.js</code> 中设置 <code>watch: true</code>。</li><li>在执行启动 Webpack 命令时，带上 <code>--watch</code> 参数，完整命令是 <code>webpack --watch</code>。</li></ul><h3 id="文件监听工作原理"><a class="header-anchor" href="#文件监听工作原理" aria-hidden="true">#</a> 文件监听工作原理</h3><p>在 Webpack 中监听一个文件发生变化的原理是定时的去获取这个文件的最后编辑时间，每次都存下最新的最后编辑时间，如果发现当前获取的和最后一次保存的最后编辑时间不一致，就认为该文件发生了变化。 配置项中的 <code>watchOptions.poll</code> 就是用于控制定时检查的周期，具体含义是每隔多少毫秒检查一次。</p><p>当发现某个文件发生了变化时，并不会立刻告诉监听者，而是先缓存起来，收集一段时间的变化后，再一次性告诉监听者。 配置项中的 <code>watchOptions.aggregateTimeout</code> 就是用于配置这个等待时间。 这样做的目的是因为我们在编辑代码的过程中可能会高频的输入文字导致文件变化的事件高频的发生，如果每次都重新执行构建就会让构建卡死。</p><p>对于多个文件来说，原理相似，只不过会对列表中的每一个文件都定时的执行检查。 但是这个需要监听的文件列表是怎么确定的呢？ 默认情况下 Webpack 会从配置的 Entry 文件出发，递归解析出 Entry 文件所依赖的文件，把这些依赖的文件都加入到监听列表中去。 可见 Webpack 这一点还是做的很智能的，不是粗暴的直接监听项目目录下的所有文件。</p><p>由于保存文件的路径和最后编辑时间需要占用内存，定时检查周期检查需要占用 CPU 以及文件 I/O，所以最好减少需要监听的文件数量和降低检查频率。</p><p>除了忽略掉部分文件的优化外，还有如下两种方法：</p><ul><li><code>watchOptions.aggregateTimeout</code> 值越大性能越好，因为这能降低重新构建的频率。</li><li><code>watchOptions.poll</code> 值越大越好，因为这能降低检查的频率。</li></ul><h2 id="进行分包处理-xxxx-xxx"><a class="header-anchor" href="#进行分包处理-xxxx-xxx" aria-hidden="true">#</a> 进行分包处理 xxxx/xxx/</h2><p>在 vue3 中,如果 app.vue 中依赖了太多的组件,可以采用</p><div class="language-javascript"><pre><code> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">&#39;./xxxx/xxx/&#39;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
     console<span class="token punctuation">.</span><span class="token punctuation">(</span>res<span class="token punctuation">.</span>xxx<span class="token punctuation">)</span>
 <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>这样的话,webpack 会对这个导入的模块进行单独的打包,从而使得进入首屏的速度变快,等到需要这个模块的时候才进行加载</p><div class="language-javascript"><pre><code>在vue3的路由中也可以进行分包<span class="token punctuation">;</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span> redirect<span class="token operator">:</span> <span class="token string">&quot;/home&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// 重定向</span>
  <span class="token punctuation">{</span>
    path<span class="token operator">:</span> <span class="token string">&quot;/home&quot;</span><span class="token punctuation">,</span>
    <span class="token function-variable function">component</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
      <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;home-chunk&quot;*/</span> <span class="token string">&quot;../xxx/xxx.vue&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token comment">//  预加载  /*   webpackPrefetch : true */   network 中可以看到文件被加载</span>
<span class="token comment">//  懒加载      network  中看不到文件被加载</span>
<span class="token comment">/* 魔法注释 */</span>
</code></pre></div></div></div><footer class="page-footer" data-v-7eddb2c4 data-v-fb8d84c6><div class="edit" data-v-fb8d84c6><div class="edit-link" data-v-fb8d84c6 data-v-1ed99556><!----></div></div><div class="updated" data-v-fb8d84c6><!----></div></footer><div class="next-and-prev-link" data-v-7eddb2c4 data-v-38ede35f><div class="container" data-v-38ede35f><div class="prev" data-v-38ede35f><a class="link" href="/interview/WebPack/" data-v-38ede35f><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="icon icon-prev" data-v-38ede35f><path d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"></path></svg><span class="text" data-v-38ede35f>基础配置</span></a></div><div class="next" data-v-38ede35f><a class="link" href="/interview/WebPack/common" data-v-38ede35f><span class="text" data-v-38ede35f>常见的Loader以及Plugin</span><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="icon icon-next" data-v-38ede35f><path d="M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"></path></svg></a></div></div></div><!--[--><!--]--></div></main></div><!----><!--]--></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"index.md\":\"992e2bc4\",\"css_advanced.md\":\"157298f3\",\"css_index.md\":\"0df1ef28\",\"browser_advanced.md\":\"a7a79457\",\"browser_index.md\":\"9938c247\",\"browser_plugin.md\":\"fd27046d\",\"git_index.md\":\"fb33a92a\",\"html_advanced.md\":\"599ad3d5\",\"html_index.md\":\"c94736b6\",\"interview_index.md\":\"07dc8acc\",\"interview_two.md\":\"0ca812f6\",\"javascript_advanced.md\":\"5c434e6d\",\"javascript_index.md\":\"d996a3fc\",\"jenkins_index.md\":\"3068e232\",\"myself_index.md\":\"340fc837\",\"myself_one.md\":\"31202d59\",\"myself_pre.md\":\"aa93248e\",\"network_index.md\":\"7251e8b9\",\"network_security.md\":\"d11dae3c\",\"node_index.md\":\"e0b22fc7\",\"node_pit.md\":\"a1502c64\",\"npm_index.md\":\"59b033b6\",\"pit_index.md\":\"603d46aa\",\"solution_index.md\":\"987aa705\",\"typescript_advanced.md\":\"431b8bba\",\"typescript_combat.md\":\"60c9a8a2\",\"typescript_index.md\":\"bd88b5a2\",\"vite_index.md\":\"d6838c03\",\"vite_optimize.md\":\"49f1bb24\",\"vue_index.md\":\"380ec221\",\"vue_reactive.md\":\"206c8e46\",\"vue_router.md\":\"0d556c25\",\"vue_source.md\":\"592e192a\",\"vue_vuex.md\":\"032502ef\",\"webpack_advanced.md\":\"ae6c63b7\",\"webpack_common.md\":\"141deb7a\",\"webpack_custom.md\":\"4827ebf8\",\"webpack_index.md\":\"49f185a6\",\"webpack_optimize.md\":\"545698f9\",\"vue_source_index.md\":\"1c12bc29\",\"vue_source_nexttick.md\":\"4533a28b\",\"vue_source_watch.md\":\"0e7342fc\"}")</script>
    <script type="module" async src="/interview/assets/app.49accf37.js"></script>
  </body>
</html>